package org.jeecg.modules.demo.Kingdee;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.dingtalkstorage_1_0.Client;
import com.aliyun.dingtalkstorage_1_0.models.*;
import com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceResponseBody;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.aliyun.teautil.models.RuntimeOptions;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jeecg.dingtalk.api.core.response.Response;
import com.kingdee.bos.webapi.sdk.K3CloudApi;
import com.taobao.api.ApiException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.constant.enums.MessageTypeEnum;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.demo.DPT.entity.Ticket;
import org.jeecg.modules.demo.DPT.job.SynTicketJob;
import org.jeecg.modules.demo.DPT.service.ITicketService;
import org.jeecg.modules.demo.DptAccount.entity.DptAccount;
import org.jeecg.modules.demo.DptAccount.service.IDptAccountService;
import org.jeecg.modules.demo.PaymentRequest.entity.PaymentRequest;
import org.jeecg.modules.demo.PaymentRequest.job.SynPaymentReqJob;
import org.jeecg.modules.demo.util.DingTalkUtil;
import org.jeecg.modules.demo.util.TicketUtil;
import org.jeecg.modules.system.entity.SysThirdAppConfig;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysThirdAppConfigService;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.service.impl.ThirdAppDingtalkServiceImpl;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.HttpURLConnection;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.jeecg.modules.demo.util.KingdeeUtil.convertExcelToMapList;

/**
 * @Description:
 * @Author: jeecg-boot
 * @Date: 2025-05-14
 * @Version: V1.0
 */
@Tag(name = "")
@RestController
@RequestMapping("/KingDee")
@Slf4j
public class KingDeeController extends JeecgController<Ticket, ITicketService> {

    @Value(value = "${jeecg.path.upload}")
    private String uploadpath;

    @Autowired
    private ISysThirdAppConfigService appConfigService;
    @Autowired
    ThirdAppDingtalkServiceImpl dingtalkService;
    @Autowired
    private ISysUserService sysUserService;

    /**
     * 匹配报表的采购订单和发货数量，下推收料通知单，写入上传文件
     *
     * @param params 上传文件的地址
     * @return
     */
    @GetMapping(value = "/processAndDownload")
    public Result<?> processAndDownloadFile(String params, HttpServletRequest request, HttpServletResponse response) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        // 提交异步任务
        executor.submit(() -> {
            try {
                process(params, request, response);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("异步任务执行异常：" + e.getMessage());
                String receiver = JwtUtil.getUserNameByToken(request);
                // 租户id
                int tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
                String dingType = MessageTypeEnum.DD.getType();
                SysThirdAppConfig config = appConfigService.getThirdConfigByThirdType(tenantId, dingType);
                if (null != config) {
                    MessageDTO messageErr = new MessageDTO(receiver, receiver, "导入收料通知单出错", "导入收料通知单出错过程出错，请联系管理员");
                    messageErr.setMsgType("text");
                    messageErr.setToAll(false);
                    dingtalkService.sendMessageResponse(messageErr, false);
                }
            }
        });
        return Result.OK("开始处理，完成后将发送钉钉消息");
    }

    public Result<?> process(String params, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String username = JwtUtil.getUserNameByToken(request);
        // 根据用户名查询用户信息
        SysUser sysUser = sysUserService.getUserByName(username);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String formattedDate = sdf.format(new Date());
        long startAll = System.currentTimeMillis();
        params = params.replace("..", "").replace("../", "");
        if (params.endsWith(SymbolConstant.COMMA)) {
            params = params.substring(0, params.length() - 1);
        }
        SsrfFileTypeFilter.checkDownloadFileType(params);
        String filePath = uploadpath + File.separator + params;
        File file = new File(filePath);
        if (!file.exists()) {
            response.setStatus(404);
            log.error("文件[" + params + "]不存在..");
            return Result.error("文件不存在");
        }

        K3CloudApi api = new K3CloudApi();
        List<Map<String, Object>> ExcelMap = List.of();
        JSONObject excelMapMerge = null;
//		  2. 文件处理逻辑（基于附件中的代码）
        // 2.1 转换Excel为Map列表
        ExcelMap = convertExcelToMapList(filePath, 0, 0, false, 0);
//			 inputStream.close();
        //将报表中的相同分录的数量合并
        excelMapMerge = new JSONObject();
        for (int i = 0; i < ExcelMap.size(); i++) {
            // i 就是当前元素的索引号（从0开始）
            Map<String, Object> item = ExcelMap.get(i);
            String materialNo = item.get("客户商品编码").toString().replaceAll(" ", "");
            String purchaseno = item.get("客户订单号").toString();
            String warehouse = item.get("仓库名称").toString();
            String count = item.get("数量").toString();
            int index = i + 1;
            BigDecimal materialCountInExcel = new BigDecimal(count);
            JSONObject purchase = excelMapMerge.getJSONObject(purchaseno);
            if (purchase == null) {
                purchase = new JSONObject();
                JSONObject material = new JSONObject();
//					 报表中的物料数量
                material.put("materialCountInExcel", materialCountInExcel);
//					 金蝶中的数量
                material.put("materialCountInKD", BigDecimal.ZERO);
//					 仓库名称
                material.put("warehouse", warehouse);
                purchase.put(materialNo, material);
                excelMapMerge.put(purchaseno, purchase);
            } else {
                JSONObject material = purchase.getJSONObject(materialNo);
                if (material == null) {
                    material = new JSONObject();
                    material.put("materialCountInExcel", materialCountInExcel);
                    material.put("materialCountInKD", BigDecimal.ZERO);
                    material.put("warehouse", warehouse);
                    purchase.put(materialNo, material);
                } else {
//						 合并重复的物料数量
                    material.put("materialCountInExcel", material.getBigDecimal("materialCountInExcel").add(materialCountInExcel));
                }
            }
        }


        // 2.3 获取采购入库单明细 将金蝶的含税单价和数量放进excelMap中
        excelMapMerge.keySet();
        // 1. 创建线程池
        ExecutorService executorQuery = Executors.newFixedThreadPool(excelMapMerge.keySet().size()); // 根据实际情况调整线程数
        List<Future<Void>> futuresQuery = new ArrayList<>();
//			 遍历报表合并的数据
        for (String purchaseno : excelMapMerge.keySet()) {
            JSONObject purchase = excelMapMerge.getJSONObject(purchaseno);
            futuresQuery.add(executorQuery.submit(() -> {
                //								 查询采购订单(明细维度，所以有多条数据)
                String jsonPurchase = String.format("{\n" +
                        "\"FormId\": \"PUR_PurchaseOrder\",\n" +
                        "\"FieldKeys\": \"FID,FMaterialId,FREMAINRECEIVEQTY,F_RBPB_WAREHOUSE,FPOOrderEntry_FEntryID,FSupplierId\",\n" +
                        "\"FilterString\": \"FBillNo = '%s'\"\n" +
                        "}", purchaseno);
                String PurchaseStr = String.valueOf(api.billQuery(jsonPurchase));
                List<JSONObject> Purchase = JSONArray.parseArray(PurchaseStr, JSONObject.class);
                if (!Purchase.isEmpty()) {
                    //							 查询供应商名称
                    String FSupplierId = Purchase.get(0).getString("FSupplierId");
                    String FSupplierjson = String.format("{\n" +
                            "    \"FormId\": \"BD_Supplier\",\n" +
                            "    \"FieldKeys\": \"FSUPPLIERID,FNUMBER,FNAME\",\n" +
                            "    \"FilterString\": \"FSUPPLIERID = %S\"\n" +
                            "}", FSupplierId);
                    String SupplierjsonStr = api.billQuery(FSupplierjson);
                    String suppliername = JSONArray.parseArray(SupplierjsonStr, JSONObject.class).get(0).getString("FNAME");

                    for (JSONObject purchaseEntry : Purchase) {
                        //								 物料id
                        String materialId = purchaseEntry.getString("FMaterialId");
                        //								 物料内码
                        String entryID = purchaseEntry.getString("FPOOrderEntry_FEntryID");
                        //								 剩余收料数量
                        String qty = purchaseEntry.getString("FREMAINRECEIVEQTY");
                        String materialjson = String.format("{\n" +
                                "    \"FormId\": \"BD_MATERIAL\",\n" +
                                "    \"FieldKeys\": \"FNUMBER\",\n" +
                                "    \"FilterString\": \"FMATERIALID = %S\"\n" +
                                "}", materialId);
                        String materialStr = api.billQuery(materialjson);
                        List<JSONObject> materials = JSONArray.parseArray(materialStr, JSONObject.class);
                        //								 查询物料编号
                        String materialNoInKD = materials.get(0).getString("FNUMBER");
                        //该供应商特殊处理
                        if (suppliername.equals("中山诚威科技有限公司")) {
                            materialNoInKD = materialNoInKD.substring(materialNoInKD.length() - 6);
                        }
                        if (purchase.getJSONObject(materialNoInKD) != null) {
                            //									 将查询到的金蝶数量放进excelMapMerge中
                            purchase.getJSONObject(materialNoInKD).put("materialCountInKD", new BigDecimal(qty));
                            purchase.getJSONObject(materialNoInKD).put("materialId", materialId);
                            purchase.getJSONObject(materialNoInKD).put("entryID", entryID);
                        }
                    }
                }
                return null;
            }));
        }
        long start = System.currentTimeMillis();
        // 3. 等待所有任务完成
        for (Future<Void> future : futuresQuery) {
            future.get();
        }
        // 4. 完成查询采购订单任务后,关闭线程池
        executorQuery.shutdown();

        //			 进行下推任务
        ExecutorService executorPush = Executors.newFixedThreadPool(excelMapMerge.keySet().size()); // 根据实际情况调整线程数
        List<Future<Void>> futuresPush = new ArrayList<>();
        for (String purchaseno : excelMapMerge.keySet()) {
            JSONObject purchase = excelMapMerge.getJSONObject(purchaseno);
            List<Map<String, Object>> finalExcelMap = ExcelMap;
            futuresPush.add(executorPush.submit(() -> {
                List entryid = new ArrayList<>();
                List<String> updateCountJsonString = new ArrayList<>();
                //						 将每张订单中的物料进行数量校验，符合下推条件的订单进行下推，否则标注差异
                String warehouse = "";
                String FNUMBER = "";
                for (String materialNo : purchase.keySet()) {
                    JSONObject material = purchase.getJSONObject(materialNo);
                    //根据仓库名称获取仓库编号
                    if (warehouse.isEmpty()) {
                        warehouse = material.getString("warehouse");
                        String Parm = String.format("{\"FormId\":\"BD_Stock\",\"FieldKeys\":\"FNAME,FNUMBER,FSTOCKID\",\"FilterString\":\"FNAME = '%S'\"}", warehouse);
                        String stockStr = api.billQuery(Parm);
                        List<JSONObject> stock = JSONArray.parseArray(stockStr, JSONObject.class);
                        FNUMBER = stock.get(0).getString("FNUMBER");
                    }
                    //							 收料单数量不能大于采购订单剩余收料数量
                    if (material.getBigDecimal("materialCountInExcel").compareTo(material.getBigDecimal("materialCountInKD")) <= 0) {
                        entryid.add(material.getString("entryID"));
                        material.put("needPush", true);
                        int materialCount = material.getBigDecimal("materialCountInExcel").intValue();
                        String materialId = material.getString("materialId");
                        // 生成更新数量json
                        updateCountJsonString.add(String.format("            {\n" +
                                "                \"FEntryID\": \"FEntryIDValue%S\",\n" +
                                "                \"FActReceiveQty\": %S,\n" +
                                "                \"FStockQty\": %S,\n" +
                                "                \"FPriceBaseQty\": %S,\n" +
                                "                \"FActlandQty\": %S,\n" +
                                "                \"FStockBaseQty\": %S,\n" +
                                "                \"FSUPDELQTY\": %S\n" +
                                "            }\n", materialId, materialCount, materialCount, materialCount, materialCount, materialCount, materialCount));
                    } else {
                        //								 无法下推，写入备注
                        material.put("remark", String.format("采购订单剩余收料数量不足(%s)", material.getBigDecimal("materialCountInKD")));
                        material.put("needPush", false);
                    }
                }
                String joinUpdateCountJson = String.join(",", updateCountJsonString);
                String pushMsg = "";
                if (!entryid.isEmpty()) {
                    String paradata = String.format("{\"EntryIds\": \"%s\",\"RuleId\": \"PUR_PurchaseOrder-PUR_ReceiveBill\"}", String.join(",", entryid));
                    String resultS = api.push("PUR_PurchaseOrder", paradata);
                    JSONObject result = JSONObject.parseObject(resultS);
                    String isSuccess = result.getJSONObject("Result").getJSONObject("ResponseStatus").getString("IsSuccess");
                    if (isSuccess.equals("true")) {
                        log.info("下推成功：" + resultS);
                        String BillNo = result.getJSONObject("Result").getJSONObject("ResponseStatus").getJSONArray("SuccessEntitys").getJSONObject(0).getString("Number");
                        String FID = result.getJSONObject("Result").getJSONObject("ResponseStatus").getJSONArray("SuccessEntitys").getJSONObject(0).getString("Id");
                        JSONArray FDetailEntity = result.getJSONObject("Result").getJSONObject("ResponseStatus").getJSONArray("SuccessEntitys").getJSONObject(0).getJSONObject("EntryIds").getJSONArray("FDetailEntity");
                        String FNote = "";
                        for (Object FENTRYIDS : FDetailEntity) {
                            String FENTRYID = FENTRYIDS.toString();
                            //									 获取收料通知单分录中的物料ID，与修改数量的json匹配替换
                            String Parm = String.format("{\"FormId\":\"PUR_ReceiveBill\",\"FieldKeys\":\"FMATERIALID,FNote\",\"FilterString\":\"FENTRYID= %S\"}", FENTRYID);
                            String materialStr = api.billQuery(Parm);
                            List<JSONObject> materials = JSONArray.parseArray(materialStr, JSONObject.class);
                            String materialId = materials.get(0).getString("FMATERIALID");
                            if (FNote.isEmpty()) {
                                FNote = materials.get(0).getString("FNote");
                            }
                            joinUpdateCountJson = joinUpdateCountJson.replace("FEntryIDValue" + materialId, FENTRYID);
                        }

                        pushMsg = "下推成功，单号：" + BillNo;

                        String json = String.format("  {\n" +
                                "\"NeedUpDateFields\": [\n" +
                                "\"F_RBPB_TWAREHOUSE\",\n" +
                                "\"FActReceiveQty\",\n" +
                                "\"FStockQty\",\n" +
                                "\"FPriceBaseQty\",\n" +
                                "\"FActlandQty\",\n" +
                                "\"FStockBaseQty\",\n" +
                                "\"FNote\",\n" +
                                "\"FStockID\",\n" +
                                "\"FSUPDELQTY\"\n" +
                                "],\n" +
                                "\"IsDeleteEntry\": \"false\",\n" +
                                "            \"SubSystemId\": \"\",\n" +
                                "            \"IsVerifyBaseDataField\": \"false\",\n" +
                                "            \"IsEntryBatchFill\": \"True\",\n" +
                                "            \"IsAutoSubmitAndAudit\": \"false\"," +
                                "            \"NeedReturnFields\": []," +
                                "\"Model\": {\n" +
                                "\"FID\": \"%s\",\n" +
                                "\"FNote\": \"%s\",\n" +
                                "\"F_RBPB_TWAREHOUSE\": {\n" +
                                "                    \"FNumber\": \"%s\"\n" +
                                "                },\n" +
                                "\"FDetailEntity\": [\n" + joinUpdateCountJson +
                                "        ]" +
                                "}\n" +
                                "}\n", FID, FNote + " " + sysUser.getRealname() + " 钉钉批量导入：" + formattedDate, FNUMBER);
                        String result1 = api.save("PUR_ReceiveBill", json);
                        log.info("修改数据结果：" + result1);
                        String Auditjson = String.format("{\"Ids\": \"%s\"}", FID);
                        String result2 = api.submit("PUR_ReceiveBill", Auditjson);
                        log.info("提交数据结果：" + result2);
                        String result3 = api.audit("PUR_ReceiveBill", Auditjson);
                        log.info("审核数据结果：" + result3);
                    } else {
                        pushMsg = "下推失败：" + resultS;
                        log.info("下推失败：" + resultS);
                    }
                }


                for (String materialNo : purchase.keySet()) {
                    String diff = "";
                    JSONObject material = purchase.getJSONObject(materialNo);
                    if (material.getString("remark") != null && !material.getString("remark").isEmpty()) {
                        diff = material.getString("remark");
                    } else {
                        diff = pushMsg;
                    }
                    // 在result列表中找到对应行并添加差异信息
                    for (Map<String, Object> row : finalExcelMap) {
                        String materialNoInExc = row.get("客户商品编码").toString().replaceAll(" ", "");
                        String purchasenoInExc = row.get("客户订单号").toString();
                        if (materialNo.equals(materialNoInExc) && purchaseno.equals(purchasenoInExc)) {
                            row.put("差异信息", diff);
                            break;
                        }
                    }
                }
                return null;
            }));
        }

        // 3. 等待所有任务完成
        for (Future<Void> future : futuresPush) {
            future.get();
        }
        // 4. 关闭线程池
        executorPush.shutdown();

        long end = System.currentTimeMillis();
        log.info("金蝶总耗时：" + (end - start) + "ms");

        //最后将差异信息写入到结果文件中
        InputStream inputStream = new FileInputStream(filePath);
        // 3. 生成结果文件流
        XSSFWorkbook workbook = new XSSFWorkbook(inputStream);

        Sheet sheet = workbook.getSheetAt(0);

        // 3.1 添加差异信息列（P列）
        Row headerRow = sheet.getRow(0);
        Cell pHeaderCell = headerRow.createCell(7);
        pHeaderCell.setCellValue("差异信息");

        // 3.2 写入差异数据
        for (int i = 0; i < ExcelMap.size(); i++) {
            Map<String, Object> rowData = ExcelMap.get(i);
            String diffInfo = rowData.get("差异信息") == null ? null : rowData.get("差异信息").toString();
            if (diffInfo != null) {
                int rowNum = i + 1;
                Row row = sheet.getRow(rowNum);
                row.createCell(7).setCellValue(diffInfo);
            }
        }
//			for (Map<String, Object> rowData : ExcelMap) {
//				String diffInfo=rowData.get("差异信息")==null?null:rowData.get("差异信息").toString();
//				if (diffInfo != null) {
//					int rowNum = (new BigDecimal( rowData.get("序号").toString())).intValue() + 6;
//					Row row = sheet.getRow(rowNum);
//					row.createCell(15).setCellValue(diffInfo);
//				}
//			}

        // 3. 添加示例数据（可选）

        // 保存修改
        FileOutputStream fos = new FileOutputStream(filePath);
        workbook.write(fos);
        fos.close();


        long endAll = System.currentTimeMillis();
        log.info("方法总耗时：" + (endAll - startAll) + "ms");

        // 租户id
        int tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
        String dingType = MessageTypeEnum.DD.getType();
        SysThirdAppConfig config = appConfigService.getThirdConfigByThirdType(tenantId, dingType);
        String mediaId = dingtalkService.uploadFile(filePath);
        String receiver = JwtUtil.getUserNameByToken(request);
        MessageDTO message = new MessageDTO(receiver, receiver, "对账单匹配完成", mediaId);
        message.setMsgType("file");
        message.setToAll(false);

        if (null != config) {
            //update-end---author:wangshuai ---date:20230224  for：[QQYUN-3440]钉钉、企业微信通过租户模式隔离 ------------
            Response<String> response1 = dingtalkService.sendMessageResponse(message, false);
            return Result.OK("发送消息成功");
        }
        return Result.ok("导入完成，点击文件下载");
    }


}
